package com.bootlever.framework.rmi.configuration;

import com.alibaba.fastjson.JSON;
import com.bootlever.framework.common.utils.IntenetUtils;
import com.bootlever.framework.common.utils.StringUtils;
import com.bootlever.framework.rmi.annotation.RmiServerResource;
import com.bootlever.framework.rmi.bean.BootleverRmiContainer;
import com.bootlever.framework.rmi.bean.RmiClient;
import com.bootlever.framework.rmi.bean.RmiInfo;
import com.bootlever.framework.rmi.bean.RmiServer;
import com.bootlever.framework.rmi.constant.RmiConstant;
import com.bootlever.framework.rmi.constant.RmiServerStatus;
import com.bootlever.framework.rmi.constant.RmiZkConstant;
import com.bootlever.framework.rmi.proxy.RmiProxy;
import com.bootlever.framework.zookeeper.templates.ZookeeperTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.zookeeper.CreateMode;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Configuration
public class RmiConfiguration implements RmiConstant{

    private static final Logger logger = LoggerFactory.getLogger(RmiConfiguration.class);

    @Value("${bootlever.app.basePackage:''}")
    private String basePackage;

    @Value("${spring.application.name:''}")
    private String springApplicationName;

    @Autowired
    private ZookeeperTemplate zookeeperTemplate;

    @Value("${bootlever.app.rmiClientPackage:''}")
    private String rmiClientPackage;

    @Bean
    public BootleverRmiContainer bootleverRmiContainer() throws IOException {
        logger.info("开始初始化BootleverRmiContainer....");
        BootleverRmiContainer container = new BootleverRmiContainer();
        String ipAddr = IntenetUtils.getLocalHostIp();
        if (StringUtils.isBlank(ipAddr)) {
            logger.error("获取本机IP地址为空");
            return container;
        }
        if (StringUtils.isBlank(basePackage)) {
            logger.error("未指定app扫包路径");
            return container;
        }
        //发布只发布基础包下的，引入的jar包的都不发布
        Reflections reflections = new Reflections(basePackage, new TypeAnnotationsScanner(),new SubTypesScanner());
        Set<Class<?>> set = Sets.newConcurrentHashSet();
        set.addAll(reflections.getTypesAnnotatedWith(RmiServerResource.class));
        if (set.isEmpty()) {
            logger.info("扫描未发现RmiServerResource类");
            //return container;
        }
        for (Class<?> clazz : set) {
            Set<Class<?>> superClasses = ReflectionUtils.getAllSuperTypes(clazz);
            Class<?> superClass = null;
            int num = 0;
            for (Class c : superClasses) {
                if (c.getAnnotation(com.bootlever.framework.rmi.annotation.RmiServer.class) != null){
                    superClass = c;
                    num ++;
                }
            }
            if (num != 1) {
                if (num == 0) {
                    logger.error("{}未配置RmiServer接口", clazz.getName());
                } else {
                    logger.error("{}未配置{}个RmiServer接口", clazz.getName(),num);
                }
                continue;
            }

            com.bootlever.framework.rmi.annotation.RmiServer rmiServerAnnotation = superClass.
                    getAnnotation(com.bootlever.framework.rmi.annotation.RmiServer.class);
            String appName = StringUtils.getNString(rmiServerAnnotation.appName());
            if (StringUtils.isBlank(appName)){
                if (StringUtils.isBlank(springApplicationName)) {
                    logger.error("{} appName为空",superClass.getName());
                    continue;
                }
                appName = springApplicationName;
            }
            if (!container.containsServer(appName)){//没有appName -- > RmiServer
                RmiServer newServer = new RmiServer();
                newServer.setAppName(appName);
                container.putServerIfAbsent(newServer);
            }
            String interfaceName = superClass.getSimpleName();//接口名称
            if (StringUtils.isNotBlank(rmiServerAnnotation.name())) {
                //name属性有
                interfaceName = StringUtils.getNString(rmiServerAnnotation.name());
            }
            RmiServer currentServer = container.getServer(appName);
            if (currentServer.getRmiInterface().get(interfaceName) != null){//有这个接口
                //一个微服务只能发布一个相同的接口
                logger.error("一个微服务只能发布一个接口，接口名称{}已经使用过",interfaceName);
                continue;
            }
            List<RmiInfo> rmiInfos = Lists.newCopyOnWriteArrayList();
            currentServer.getRmiInterface().putIfAbsent(interfaceName,rmiInfos);
            //加入RmiInfo
            RmiServerResource rmiServerResource = clazz.getAnnotation(RmiServerResource.class);
            RmiInfo rmiInfo = new RmiInfo();
            rmiInfo.setPort(rmiServerResource.registryPort());
            rmiInfo.setName(interfaceName);
            String value = rmiServerResource.value();
            if (!value.startsWith("/")){
                value = "/" + value;
            }
            rmiInfo.setAddress(RMI_LOCAL_ADDRESS + ":" + rmiServerResource.registryPort() + "/" + appName + value);
            rmiInfo.setStatus(RmiServerStatus.OK.name());
            rmiInfo.setId(ipAddr + ":" + rmiServerResource.registryPort());
            StringBuffer sb = new StringBuffer(100);
            sb.append(RmiZkConstant.ROOT_PATH).append("/").append(appName).append("/").append(interfaceName).append("/")
                    .append(ipAddr).append(":").append(rmiInfo.getPort());
            try {
                //注册
                registryRmiServer(rmiInfo,clazz,sb.toString());
                //注册成功加入服务列表
                currentServer.getRmiInterface().get(interfaceName).add(rmiInfo);
                logger.info("注册成功：{}", JSON.toJSON(rmiInfo));
            } catch (Exception e) {
                logger.error("注册失败：", e);
            }
        }
        //初始化rmiClient,扫描basePackage和rmiClientPackage下的
        Set<Class<?>> clientSet = Sets.newConcurrentHashSet();
        try {
            if (StringUtils.isNotBlank(rmiClientPackage)){
                for (String packageName : rmiClientPackage.split(",")){
                    reflections = new Reflections(packageName);
                    clientSet.addAll(reflections.getTypesAnnotatedWith(com.bootlever.framework.rmi.annotation.RmiServer.class));
                }
            }
        } catch (Exception e){
            logger.error("初始化rmiClient出错", e);
        }
        if (clientSet.isEmpty()){
            return container;
        }
        for (Class<?> client : clientSet) {
            if (client.getAnnotation(com.bootlever.framework.rmi.annotation.RmiServer.class) == null) {
                continue;
            }

            com.bootlever.framework.rmi.annotation.RmiServer rmiServer =
                    client.getAnnotation(com.bootlever.framework.rmi.annotation.RmiServer.class);
            String appName = rmiServer.appName();
            if (container.getClient(appName) == null) {
                RmiClient newRmiClient = new RmiClient();
                newRmiClient.setAppName(appName);
                container.putClientIfAbsent(newRmiClient);
            }
            String name = rmiServer.name();
            if (StringUtils.isBlank(name)){
                name = client.getSimpleName();//name为空，就用接口名称
            }
            //此时增加为这个接口创建一个Proxy对象，并放到BeanFactory中
            container.getRmiInterface().putIfAbsent(name, client);

            if (container.getClient(appName).getOkStatusInfos().get(name) == null){
                Map<String, List<RmiInfo>> okInfos = container.getClient(appName).getOkStatusInfos();
                okInfos.putIfAbsent(name, Lists.newCopyOnWriteArrayList());
            }
            if (container.getClient(appName).getBusyStatusInfos().get(name) == null){
                Map<String, List<RmiInfo>> busyStatusInfos = container.getClient(appName).getBusyStatusInfos();
                busyStatusInfos.putIfAbsent(name, Lists.newCopyOnWriteArrayList());
            }
            StringBuffer sb = new StringBuffer(100);
            sb.append(RmiZkConstant.ROOT_PATH).append("/").append(appName).append("/").append(name);
            logger.info("服务地址为{}", sb.toString());
            try {
                List<RmiInfo> rmiInfos = getRmiInfos(sb.toString());
                if (rmiInfos != null && rmiInfos.size()>0){
                    for (RmiInfo rmiInfo : rmiInfos){
                        putRmiClient(container.getClient(appName),rmiInfo);
                    }
                }
            } catch (Exception e) {
                logger.error("获取RmiClient失败", e);
            }
        }
        /*if (logger.isInfoEnabled()){
            logger.info("BootleverContainer:{}",JSON.toJSON(container));
        }*/
        return container;
    }

    /**
     * 增加RmiClient
     * @param rmiClient
     * @param rmiInfo
     */
    private void putRmiClient(RmiClient rmiClient, RmiInfo rmiInfo) {
        if (RmiServerStatus.OK.name().equals(rmiInfo.getStatus())) {
            if (rmiClient.getOkStatusInfos().get(rmiInfo.getName()) == null){
                List<RmiInfo> newRmiInfoList = Lists.newCopyOnWriteArrayList();
                rmiClient.getOkStatusInfos().putIfAbsent(rmiInfo.getName(),newRmiInfoList);
            }
            rmiClient.getOkStatusInfos().get(rmiInfo.getName()).add(rmiInfo);
            return;
        }
        if (RmiServerStatus.BUSY.name().equals(rmiInfo.getStatus())){
            if (rmiClient.getBusyStatusInfos().get(rmiInfo.getName()) == null){
                List<RmiInfo> newRmiInfoList = Lists.newCopyOnWriteArrayList();
                rmiClient.getBusyStatusInfos().putIfAbsent(rmiInfo.getName(),newRmiInfoList);
            }
            rmiClient.getBusyStatusInfos().get(rmiInfo.getName()).add(rmiInfo);
            return;
        }
    }

    private List<RmiInfo> getRmiInfos(String path) throws Exception {
        List<RmiInfo> rmiInfos = Lists.newCopyOnWriteArrayList();
        List<String> list = zookeeperTemplate.getChildren(path);
        if (list == null || list.size()==0) {
            return rmiInfos;
        }
        for (String childPath : list) {
            String data = zookeeperTemplate.getData(path + "/" + childPath);
            if (StringUtils.isBlank(data)){
                logger.error("{}路径下内容为空",childPath);
                continue;
            }
            RmiInfo rmiInfo = JSON.parseObject(data,RmiInfo.class);
            if (rmiInfo != null) {
                rmiInfos.add(rmiInfo);
            }
        }
        return rmiInfos;
    }

    /**
     * 注册RmiServer
     * @param rmiInfo
     * @param implClass
     * @param zookeeperPath
     * @throws Exception
     */
    private void registryRmiServer(RmiInfo rmiInfo, Class implClass, String zookeeperPath) throws Exception {
        //bind服务
        bindRmiServer(rmiInfo,implClass);
        //注册到zookeeper
        registryZookeeper(zookeeperPath,rmiInfo);
    }

    /**
     * 绑定服务到端口
     * @param rmiInfo
     * @param implClass
     * @throws RemoteException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws AlreadyBoundException
     * @throws MalformedURLException
     */
    private void bindRmiServer(RmiInfo rmiInfo, Class implClass) throws RemoteException, IllegalAccessException, InstantiationException, AlreadyBoundException, MalformedURLException {
       // LocateRegistry.createRegistry(rmiInfo.getPort());
       // Naming.bind(rmiInfo.getAddress(), (Remote) implClass.newInstance());
        Registry registry = null;
        try {
            registry = LocateRegistry.getRegistry(rmiInfo.getPort()); //如果该端口未被注册，则抛异常
            registry.list(); //拿到该端口注册的rmi对象
        } catch (final Exception e) {
            try {
                registry = LocateRegistry.createRegistry(rmiInfo.getPort());//捕获异常，端口注册
            } catch (final Exception ee) {
                ee.printStackTrace();
            }
        }
        //registry.rebind(rmiInfo.getAddress(), (Remote) implClass.newInstance());
        Naming.bind(rmiInfo.getAddress(), (Remote) implClass.newInstance());
    }

    /**
     * 写入zookeeper节点
     * @param path
     * @param rmiInfo
     * @throws Exception
     */
    private void registryZookeeper(String path, RmiInfo rmiInfo) throws Exception {
        zookeeperTemplate.createPathWithParent(path, JSON.toJSON(rmiInfo).toString(), CreateMode.EPHEMERAL);
    }
}
